package com.vict.config;

import com.vict.GuiApplication;
import com.vict.util.TimeUtil;
import lombok.Data;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;

import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.Optional;
import java.util.concurrent.*;

@Plugin(name = "Statistics", category = "Core", elementType = "appender", printObject = true)
public class StatisticsAppender extends AbstractAppender {
    /** 日志级别大于等于此级别及以上会进行判断错误。默认：ERROR */
    private String failedOnLogLevel;
    /** 指定时间内，出现多少次该日志级别，会被认为是错误。默认：10 */
    private Integer failedOnLogLevelCount;
    /** 该日志级别以上持续出现多长时间，会被认为是错误。默认:30000 */
    private Integer failedOnLogLevelInMisSecond;
    /** 当连续小于该日志级别多长时间后，恢复为正常状态。默认：120000 */
    private Integer recoveryOnLessLogLevelInMisSecond;


    protected StatisticsAppender(String name, Filter filter, Layout<? extends Serializable> layout,
                                 String failedOnLogLevel,
                                 Integer failedOnLogLevelCount,
                                 Integer failedOnLogLevelInMisSecond,
                                 Integer recoveryOnLessLogLevelInMisSecond) {
        super(name, filter, layout);
        this.failedOnLogLevel = failedOnLogLevel;
        this.failedOnLogLevelCount = failedOnLogLevelCount;
        this.failedOnLogLevelInMisSecond = failedOnLogLevelInMisSecond;
        this.recoveryOnLessLogLevelInMisSecond = recoveryOnLessLogLevelInMisSecond;
    }

    public static ConcurrentLinkedQueue<LogDto> logList = new ConcurrentLinkedQueue<LogDto>();
    public static ExecutorService logThreadPool = new ThreadPoolExecutor(1, 1, 99999, TimeUnit.DAYS, new ArrayBlockingQueue<>(20));


    @Data
    public static class LogDto{
        private Long time;
        private String level;
        private String threadId;
        private String threadName;
        private String errInfo;
        private String text;
    }

    @Override
    public void append(LogEvent logEvent) {
        String level = Optional.ofNullable(logEvent).map(o-> o.getLevel()).map(o-> o.name()).orElse(""); // 日志级别
        String threadId = Optional.ofNullable(logEvent).map(o-> o.getThreadId()).map(o-> o.toString()).orElse(""); // 线程id
        String threadName = Optional.ofNullable(logEvent).map(o-> o.getThreadName()).orElse(""); // 线程名
        String errInfo = Optional.ofNullable(logEvent).map(o-> o.getThrown()).map(o-> getExceptionAsString(o)).orElse("");
        String text = Optional.ofNullable(logEvent).map(o-> o.getMessage()).map(o-> o.getFormattedMessage()).orElse(""); // 日志文本信息
        String className = Optional.ofNullable(logEvent).map(o-> o.getSource()).map(o-> o.getClassName()).orElse(""); // 日志文本信息
        String methodName = Optional.ofNullable(logEvent).map(o-> o.getSource()).map(o-> o.getMethodName()).orElse(""); // 日志文本信息
        String lineNumber = Optional.ofNullable(logEvent).map(o-> o.getSource()).map(o-> o.getLineNumber()).map(o-> o.toString()).orElse(""); // 日志文本信息

        String str = TimeUtil.getTimeStr(null) + " " + level + " [" + threadName + "]" + " " +
                className + "." + methodName + "(" + lineNumber + ") " + text;
        str = str + errInfo;
        str = str + "\r\n";
        String finalStr = str;
        Optional.ofNullable(GuiApplication.guiApplication).map(o-> o.getLogGui()).map(o-> o.getLogText())
                .ifPresent(o-> o.append(finalStr));

    }

    private static String getExceptionAsString(Throwable throwable) {
        // 使用 StringWriter 将异常信息转换为字符串
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter);
        throwable.printStackTrace(printWriter);
        return stringWriter.toString();
    }

    @PluginFactory
    public static StatisticsAppender createAppender(@PluginAttribute("name") String name,
                                                    @PluginElement("Filter") final Filter filter,
                                                    @PluginElement("Layout") Layout<? extends Serializable> layout,
                                                    @PluginAttribute("failedOnLogLevel") String failedOnLogLevel,
                                                    @PluginAttribute("failedOnLogLevelCount") Integer failedOnLogLevelCount,
                                                    @PluginAttribute("failedOnLogLevelInMisSecond") Integer failedOnLogLevelInMisSecond,
                                                    @PluginAttribute("recoveryOnLessLogLevelInMisSecond") Integer recoveryOnLessLogLevelInMisSecond) {
        if (name == null) {
            LOGGER.error("No name provided for MyCustomAppenderImpl");
            return null;
        }
        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }
        if (failedOnLogLevel == null) {
            failedOnLogLevel = "ERROR";
        }
        if (failedOnLogLevelCount == null) {
            failedOnLogLevelCount = 10;
        }
        if (failedOnLogLevelInMisSecond == null) {
            failedOnLogLevelInMisSecond = 30000;
        }
        if (recoveryOnLessLogLevelInMisSecond == null) {
            recoveryOnLessLogLevelInMisSecond = 120000;
        }
        return new StatisticsAppender(name, filter, layout, failedOnLogLevel, failedOnLogLevelCount, failedOnLogLevelInMisSecond, recoveryOnLessLogLevelInMisSecond);
    }
}
